﻿#ifndef __INC_QGUWGS_H__
#define __INC_QGUWGS_H__

#ifdef _MSC_VER
#pragma once
#endif

#include <functional>

// 
class qgWgs;
class qwCtrl;
class qwForm;


//////////////////////////////////////////////////////////////////////////
// widget data

#define QCH_WIDGET(type)		public: enum { WidgetType = type }

// type
enum qgWidgetType
{
	QGW_CONTROL,

	QGW_FORM,
	QGW_PANEL,

	QGW_DIVIDE,
	QGW_LABEL,
	QGW_BUTTON,
	QGW_SCROLL,
	QGW_LIST,
	QGW_COMBO,
	QGW_PROGRESS,
	QGW_TEXTLINE,

	QGW_RIBBON_BUTTON,
	QGW_RIBBON_COMBO,

	QGW_MAX_VALUE
};

// status
enum qgWidgetStatus
{
	QGWS_NORMAL,
	QGWS_SELECT,
	QGWS_OVER,
	QGWS_DISABLE,
	QGWS_MAX_VALUE
};

// place
enum qgWidgetPlace
{
	QGWPLACE_LEFT,
	QGWPLACE_CENTER,
	QGWPLACE_RIGHT,
};

// align
enum qgWidgetAlign
{
	QGWALIGN_LEFT = 0,
	QGWALIGN_RIGHT = 0x1,
	QGWALIGN_CENTER = 0x2,
	QGWALIGN_TOP = 0,
	QGWALIGN_BOTTOM = 0x10,
	QGWALIGN_VCENTER = 0x20,
};

// direction
enum qgWidgetDirection
{
	QGWDIR_FRONT,
	QGWDIR_REAR,
	QGWDIR_LEFT,
	QGWDIR_RIGHT,
	QGWDIR_UP,
	QGWDIR_DOWN,

	QGWDIR_VERT,
	QGWDIR_HORIZ,
};

// element
enum qgWidgetElement
{
	QGWELM_NORMAL,
	QGWELM_SELECT,
	QGWELM_OVER,
	QGWELM_DISABLE,

	// divide
	QGWELM_DIVIDE_0,
	QGWELM_DIVIDE_1,
	QGWELM_DIVIDE_2,
	QGWELM_DIVIDE_3,

	// progress
	QGWELM_PROGRESS_VALUE,

	// SCROLL
	QGWELM_SCROLL_IND_UP,
	QGWELM_SCROLL_IND_DOWN,
	QGWELM_SCROLL_IND_THUMB,
	QGWELM_SCROLL_IND_TRACK,

	// LIST
	QGWELM_LIST_SELECT_ACTIVE,
	QGWELM_LIST_SELECT_INACTIVE,

	// COMBO
	QGWELM_COMBO_OPEN,		// opened drop area
	QGWELM_COMBO_DOWN,

	// TEXTLINE
	QGWELM_TEXTLINE_DISP,
	QGWELM_TEXTLINE_CARET,
	QGWELM_TEXTLINE_SELECT,
	QGWELM_TEXTLINE_INVERT,

	// PANEL
	QGWELM_PANEL_BACKGROUND,

	// FORM
	QGWELM_FORM_ICON,
	QGWELM_FORM_SIZER,

	QGWELM_MAX_VALUE
};

// resource property
struct qbWidgetResource
{
	tpl::ColorValue	color;
	tpl::Quad		coord;
	bool			usetex;
};

// text info
struct qbWidgetTextInfo
{
	kint			size;
	kint			align;
	tpl::ColorValue	color;
	tpl::Point		anch;
};

// callback classes

// common event
typedef tpl::Function<void(qwCtrl*)> qwEventFunc;
typedef tpl::Function<void(qwCtrl*, const tpl::Rect&)> qwEventDrawFunc;
typedef tpl::Function<void(qwCtrl*, kint index, kint x, kint y)> qwEventDragFunc;
typedef tpl::Function<void(qwCtrl*, bool)> qwEventBoolFunc;
typedef tpl::Function<void(qwCtrl*, kint)> qwEventIntFunc;
typedef tpl::Function<void(qwCtrl*, const char*, kint)> qwEventTextFunc;
typedef tpl::Function<void(qwCtrl*, const tpl::Rect&)> qwEventLayoutFunc;

// container
struct qbWidgetContainer
{
	virtual qwCtrl* GetControl(const char* name) = 0;
	virtual qwCtrl* FindControl(kint id) = 0;

	virtual qwCtrl* GetControlNth(kint nth) = 0;
	virtual kint IndexControlOf(qwCtrl* cx) = 0;

	virtual bool AddControl(qwCtrl* cx) = 0;
	virtual bool RemoveControl(qwCtrl* cx) = 0;

	virtual void UnsafeAddControl(qwCtrl* cx) = 0;
	virtual bool UnsafeRemoveControl(qwCtrl* cx) = 0;

	virtual void ClearControls() = 0;
	virtual void SortControls(qwCtrl* primary) = 0;
};


//////////////////////////////////////////////////////////////////////////
// widget, control base

typedef qwCtrl qgWidgetObj;

//
class KAPI qwCtrl :public kObj, public qbSevHandler
{
	KCH_SUBCLASS(qwCtrl, kObj);
	KCH_FRIEND(qgWgs);
	KCH_FRIEND(qgWgsImpl);

public:
	typedef tpl::BlueStr<64> NameString;

protected:
	kint				_id;
	float				_adv;

	struct
	{
		bool				enable : 1;
		bool				visible : 1;
		bool				input : 1;
		bool				fill : 1;
		bool				anchor : 1;
		bool				shadow : 1;
		bool				border : 1;
		bool				sunken : 1;
		bool				extinfo : 1;
		bool				rdonly : 1;

		bool				sizable : 1;
		bool				movable : 1;
		bool				sizing : 1;
		bool				moving : 1;

		bool				accept : 1;
		bool				cancel : 1;

		bool				ribbon : 1;
		bool				menu : 1;
		bool				ctnr : 1;

		kuint				reserved : 13;
	}					_insts;

	NameString			_name;
	qgWidgetType		_type;

	tpl::Rect			_bound;
	tpl::Rect			_client;

	qgWidgetStatus		_status;
	kint				_depth;
	bool				_refresh;

	qgTextureRef		_tex;
	qbWidgetResource	_res[QGWS_MAX_VALUE];
	tpl::ColorValue		_color_shadow;
	tpl::ColorValue		_color_border;

	qgFontRef			_font;
	tpl::String			_text_str;
	qbWidgetTextInfo	_text_info;

	qwCtrl*				_parent;
	qgWgs*				_shed;

	qwEventDrawFunc		_ecb_draw;
	qwEventFunc			_ecb_click;
	qwEventLayoutFunc	_ecb_layout_changed;
	qwEventTextFunc		_ecb_text_changed;

protected:
	explicit qwCtrl(qgWidgetType type);
	~qwCtrl();

public:
	kint GetId() { return _id; }
	float GetAdvance() { return _adv; }
	const NameString& GetName() { return _name; }
	qgWidgetType GetType() { return _type; }

	const tpl::Rect& GetBound() { return _bound; }
	const tpl::Rect& GetClient() { return _client; }

	bool IsEnable() { return _insts.enable; }
	bool IsVisible() { return _insts.visible; }
	bool IsContainer() { return _insts.ctnr; }
	bool IsRibbon() { return _insts.ribbon; }
	bool IsAccept() { return _insts.accept; }
	bool IsCancel() { return _insts.cancel; }
	bool CanFill() { return _insts.fill; }
	bool CanInput() { return _insts.input; }
	bool CanSize() { return _insts.sizable; }
	bool CanMove() { return _insts.movable; }
	bool HasAnchor() { return _insts.anchor; }
	bool HasShadow() { return _insts.shadow; }
	bool HasBorder() { return _insts.border; }
	bool HasSunken() { return _insts.sunken; }
	bool HasExtInfo() { return _insts.extinfo; }
	bool HasText() { return _text_str.IsHave(); }
	bool ReadOnly() { return _insts.rdonly; }
	bool IsAlive() { return _insts.enable && _insts.visible && _insts.input; }

	qgTextureRef GetTexture() { return _tex; }
	const qbWidgetResource& GetResource() { return _res[_status]; }
	K_UNSAFE virtual const qbWidgetResource& GetResource(qgWidgetElement nth);
	const tpl::ColorValue& GetShadowColor() { return _color_shadow; }
	const tpl::ColorValue& GetBorderColor() { return _color_border; }

	qgFontRef GetFont() { return _font; }
	const qbWidgetTextInfo& GetTextInfo() { return _text_info; }
	const tpl::String& GetText() { return _text_str; }
	const char* GetTextData() { return _text_str.Data(); }
	kint GetTextLength() { return _text_str.Length(); }

	//
	void SetBound(kint x, kint y, kint w, kint h);
	void SetLocation(kint x, kint y);
	void SetSize(kint w, kint h);

	void SetVisible(bool value) { _insts.visible = value; }
	void SetEnable(bool value) { _insts.enable = value; }
	void SetFillMode(bool isfill) { _insts.fill = isfill; }
	void SetAcceptMode(bool value) { _insts.accept = value; }
	void SetCancelMode(bool value) { _insts.cancel = value; }
	void SetInputMode(bool isinput) { _insts.input = isinput; }
	void SetSizeMode(bool isenable) { _insts.sizable = isenable; }
	void SetMoveMode(bool isenable) { _insts.movable = isenable; }
	void SetShadowMode(bool isenable) { _insts.shadow = isenable; }
	void SetBorderMode(bool isenable) { _insts.border = isenable; }
	void SetSunkenMode(bool isenable) { _insts.sunken = isenable; }
	void SetExtInfo(bool isenable) { _insts.extinfo = isenable; }
	void SetReadOnly(bool readonly) { _insts.rdonly = readonly; }

	void SetTexture(qgTexture* tex) { _tex = tex; }
	void OpenTexture(const char* filename);
	virtual bool SetResource(qgWidgetElement nth, const qbWidgetResource& res);
	void SetShadowColor(const tpl::ColorValue& value) { _color_shadow = value; }
	void SetBorderColor(const tpl::ColorValue& value) { _color_border = value; }

	void SetFont(qgFont* font);
	void SetText(const char* str);
	void SetTextL(const kwchar* uni);
	void SetTextSize(kint size);
	void SetTextAlign(kint align);
	void SetTextColor(const tpl::ColorValue& color);

	bool ReplaceColor(qgWidgetElement nth, const tpl::ColorValue& value);

	//
	qwCtrl* GetParent() { return _parent; }
	qgWgs* GetShed() { return _shed; }
	virtual qbWidgetContainer* GetContainer() { return NULL; }

	void SetCustomDraw(const qwEventDrawFunc& cb) { _ecb_draw = cb; }
	void SetEventClick(const qwEventFunc& cb) { _ecb_click = cb; }
	void SetEventLayoutChanged(const qwEventLayoutFunc& cb) { _ecb_layout_changed = cb; }
	void SetEventTextChanged(const qwEventTextFunc& cb) { _ecb_text_changed = cb; }

	//
	void SetBound(const tpl::Rect& bound);
	void SetLocation(const tpl::Point& pt) { SetLocation(pt.x, pt.y); }
	void SetSize(const tpl::Point& pt) { SetSize(pt.x, pt.y); }

public:		// internal
	qgWidgetStatus GetStatus() { return _status; }
	kint GetDepth() { return _depth; }
	bool NeedRefresh() { return _refresh; }

	void SetId(kint id) { _id = id; }
	void SetName(const char* name) { _name = name; }
	void SetDepth(kuint d) { _depth = d; }
	void SetAdvance(float adv) { _adv = adv; }
	void SetStatus(qgWidgetStatus status);
	void SetRefresh(bool value = true) { _refresh = value; }
	void ResetRefresh() { _refresh = false; }

	void SetParent(qwCtrl* parent);
	void SetShed(qgWgs* shed);

	//
	void BuildBound(bool clip = true);
	void BuildTextAnch();

	bool TestPick(kint x, kint y, bool isclient = false);
	tpl::Rect CalcBound(const tpl::Rect& bound);
	tpl::Point CalcAlign(const tpl::Point& size);

public:		// internal
	virtual bool OnUpdate(float adv);
	virtual void OnDraw();

protected:
	virtual void OnInitialize();

	virtual void OnChangeStatus(qgWidgetStatus status) {}
	virtual void OnChangeFocus(bool focus) {}
	virtual void OnChangeFont(qgFont* font) {}
	virtual void OnChangeParent(qwCtrl* parent) {}

	virtual void OnTextChanged(const char* str, kint len) {}

public:
	static tpl::Point CalcAlign(kint align, const tpl::Point& size, const tpl::Rect& bound);
	static void SortWidgets(kint count, qwCtrl** ctrls);
};


//////////////////////////////////////////////////////////////////////////
// shed
class KAPI qgWgs : public qgObj, public qbWidgetContainer
{
	KCH_SUBCLASS(qgWgs, qgObj);

public:
	typedef tpl::VecArray<qbWidgetResource> ResourceArray;

protected:
	struct
	{
		bool			visible : 1;
		bool			enable : 1;
		bool			input : 1;

		kuint			reserved : 29;
	}				_insts;

	kint			_last_id;
	kint			_last_depth;

	tpl::Rect		_bound;

	float			_blend_speed;
	kbyte			_alpha_override;

	qgFontRef		_font;
	qgTextureRef	_tex[QGW_MAX_VALUE];
	ResourceArray	_res;

	qwEventBoolFunc	_ecb_modal_changed;
	qwEventFunc		_ecb_foreground_changed;

protected:
	qgWgs();
	~qgWgs();

public:
	qbWidgetContainer* GetContainer() { return this; }

	//
	bool IsVisible() { return _insts.visible; }
	bool IsEnable() { return _insts.enable; }
	kint GetLastId() { return _last_id; }
	kint GetLastDepth() { return _last_depth; }

	const tpl::Rect& GetBound() { return _bound; }
	float GetBlendSpeed() { return _blend_speed; }
	kbyte GetAlphaOverride() { return _alpha_override; }

	qgFontRef GetFont() { return _font; }
	qgTextureRef GetTexture(qgWidgetType type = QGW_CONTROL) { return _tex[type]; }
	K_UNSAFE const qbWidgetResource& GetResource(qgWidgetElement nth) { return _res[nth]; }

	//
	kint AddResource(const qbWidgetResource& res);
	bool SetResource(qgWidgetElement nth, const qbWidgetResource& res);
	bool SetTexture(qgWidgetType type, qgTexture* tex);
	bool OpenTexture(qgWidgetType type, const char* filename);

	//
	void SetVisible(bool v) { _insts.visible = v; }
	void SetEnable(bool v) { _insts.enable = v; }
	void SetBlendSpeed(float spd) { _blend_speed = spd; }
	void SetAlphaOverride(kbyte v = 0) { _alpha_override = v; /*0=no ovr*/ }

	//
	void SetEventModalChanged(const qwEventBoolFunc& cb) { _ecb_modal_changed = cb; }
	void SetEventForegroundChanged(const qwEventFunc& cb) { _ecb_foreground_changed = cb; }

	virtual void SetCustomEvent(const char* name, const qwEventFunc& fn) = 0;
	virtual bool RemoveCustomEvent(const char* name) = 0;
	virtual const qwEventFunc* GetCustomEvent(const char* name) = 0;

	//
	virtual void Update(float adv);
	virtual void Draw();

	virtual void SetForeground(qwCtrl* w) = 0;
	virtual void SetFocus(qwCtrl* w) = 0;
	virtual bool SetModal(qwCtrl* w) = 0;
	virtual bool TestModal(qwCtrl* w) = 0;

	// [qbWidgetContainer]
	// not implement here
	// [qbWidgetContainer]

	//
	void DrawAnchText(qgFont* font, const char* str, const tpl::Rect& bound, const qbWidgetTextInfo& info, kint flags);
	void DrawFillRect(const tpl::Rect& bound, const qbWidgetResource& res, qgTexture* tex = NULL);
	void DrawFillRect(const tpl::Rect& bound, const tpl::ColorValue& color, qgTexture* tex = NULL, const tpl::Quad* coord = NULL);
	void DrawEmptyRect(const tpl::Rect& bound, const tpl::ColorValue& color);
	void DrawLine(kint x1, kint y1, kint x2, kint y2, const tpl::ColorValue& color);

	//
	qwCtrl* CreateControl(qgWidgetType type, kint id, const tpl::Rect& bound, qbWidgetContainer* ctn = NULL);
	qwCtrl* AddDivide(kint id, kint x, kint y, kint size);
	qwCtrl* AddLabel(kint id, kint x, kint y, kint w, kint h, const char* text = NULL);
	qwCtrl* AddButton(kint id, kint x, kint y, kint w, kint h, const char* text = NULL);
	qwCtrl* AddProgress(kint id, kint x, kint y, kint w, kint h, kint minv, kint maxv);
	qwCtrl* AddScroll(kint id, kint x, kint y, kint h, kint size);
	qwCtrl* AddList(kint id, kint x, kint y, kint w, kint h);
	qwCtrl* AddCombo(kint id, kint x, kint y, kint w, kint h, kint droplen);
	qwCtrl* AddTextLine(kint id, kint x, kint y, kint w, kint h, const char* text = NULL);
	qwCtrl* AddPanel(kint id, kint x, kint y, kint w, kint h);
	qwCtrl* AddForm(kint id, kint x, kint y, kint w, kint h, const char* text = NULL);
	qwCtrl* AddRibbonButton(kint id, kint x, kint y, qgTexture* icon, const char* text = NULL);
	qwCtrl* AddRibbonCombo(kint id, kint x, kint y, kint w, qgTexture* icon, kint dropline);

	//
	kint ReadFile(const char* filename);
	kint ReadData(kpointer data, kint size);

protected:
	virtual void SetControl(qwCtrl* w, qgWidgetStatus status) = 0;

public:
	static qgWgs* New();
	static qgWgs* New(const char* filename);
};


//////////////////////////////////////////////////////////////////////////
// divide
class KAPI qwDivide : public qwCtrl
{
	KCH_SUBCLASS(qwDivide, qwCtrl);

protected:
	kint				_thick;
	kint				_count;
	bool				_horiz;

protected:
	qwDivide(qgWidgetType type = QGW_DIVIDE);
	~qwDivide() {}

public:
	kint GetThick() { return _thick; }
	kint GetCount() { return _count; }
	void SetThick(kint v) { if (v > 0) _thick = v; }
	void SetCount(kint v) { _count = K_CLAMP(v, 1, 4); }
	void SetHorizonal() { _horiz = true; }
	void SetVertical() { _horiz = false; }

protected:
	K_OVR void OnInitialize();

	K_OVR bool OnUpdate(float adv);
	K_OVR void OnDraw();
};


//////////////////////////////////////////////////////////////////////////
// label
class KAPI qwLabel : public qwCtrl
{
	KCH_SUBCLASS(qwLabel, qwCtrl);

protected:
	qwLabel(qgWidgetType type = QGW_LABEL);
	~qwLabel() {}

protected:
	K_OVR bool OnUpdate(float adv);
	K_OVR void OnDraw();
};


//////////////////////////////////////////////////////////////////////////
// button
class KAPI qwButton : public qwLabel
{
	KCH_SUBCLASS(qwButton, qwLabel);

protected:
	tpl::BlendColor		_blend;

protected:
	qwButton(qgWidgetType type = QGW_BUTTON);
	~qwButton() {}

protected:
	K_OVR void OnInitialize();

	K_OVR bool OnUpdate(float adv);
	K_OVR void OnDraw();

	K_OVR void OnChangeStatus(qgWidgetStatus status);

	K_OVR bool OnMouseDown(kint x, kint y, kushort button);
	K_OVR bool OnMouseUp(kint x, kint y, kushort button);
};


//////////////////////////////////////////////////////////////////////////
// progress
class KAPI qwProgress : public qwLabel
{
	KCH_SUBCLASS(qwProgress, qwLabel);

public:
	struct ValueBlend
	{
		float			width;
		float			last;
		float			lerp;
		float			speed;
		float			advance;
	};

protected:
	tpl::Rect			_bback;
	tpl::Rect			_bvalue;
	qbWidgetResource	_rvalue;

	kint				_pos;
	tpl::Point			_range;

	bool				_drag;
	tpl::Point			_margin;

	ValueBlend			_blend;

	qwEventIntFunc		_ecb_position_changed;

protected:
	qwProgress(qgWidgetType type = QGW_PROGRESS);
	~qwProgress() {}

public:
	kint GetPosition() { return _pos; }
	kint GetCenter() { return _range.min + GetLength() / 2; }
	kint GetLength() { return _range.max - _range.min; }
	const tpl::Point& GetRange() { return _range; }
	const tpl::Point& GetMargin() { return _margin; }
	K_OVR const qbWidgetResource& GetResource(qgWidgetElement nth);

	void SetPosition(kint pos, bool initvalue = false);
	void SetPosition(qgWidgetPlace place);
	void SetRange(const tpl::Point& p);
	void SetMargin(const tpl::Point& p) { _margin = p; }
	K_OVR bool SetResource(qgWidgetElement nth, const qbWidgetResource& res);

	void SetEventPositionChanged(const qwEventIntFunc& cb) { _ecb_position_changed = cb; }

protected:
	K_OVR void OnInitialize();

	K_OVR bool OnUpdate(float adv);
	K_OVR void OnDraw();

	K_OVR bool OnMouseMove(kint x, kint y, const tpl::Point& relative, kushort state);
	K_OVR bool OnMouseDown(kint x, kint y, kushort button);
	K_OVR bool OnMouseUp(kint x, kint y, kushort button);

	//
	kint ValidatePosition(kint xloc);
};


//////////////////////////////////////////////////////////////////////////
// scroll
class KAPI qwScroll : public qwCtrl
{
	KCH_SUBCLASS(qwScroll, qwCtrl);
	KCH_FRIEND(qwList);
	KCH_FRIEND(qwCombo);

public:
	enum Indicator
	{
		IndUp,
		IndDown,
		IndThumb,
		IndTrack,

		IndNone,
		IndRepUp,
		IndRepDown,
	};

protected:
	tpl::Rect			_binds[4];
	qbWidgetResource	_rinds[4];

	kint				_pos;
	kint				_pagesize;
	kint				_count;

	bool				_drag;
	bool				_thumb;
	bool				_thumb_refresh;

	Indicator			_indicator;
	float				_ind_adv;

	kint				_save_scrl;
	tpl::Point			_save_pt;

	qwEventIntFunc		_ecb_position_changed;

protected:
	qwScroll(qgWidgetType type = QGW_SCROLL);
	~qwScroll() {}

public:
	kint GetPosition() { return _pos; }
	kint GetPageSize() { return _pagesize; }
	kint GetCount() { return _count; }
	K_OVR const qbWidgetResource& GetResource(qgWidgetElement nth);

	void SetPosition(kint pos, bool initvalue = false);
	void SetPosition(qgWidgetPlace place);
	void SetPageSize(kint size);
	void SetCount(kint count);
	K_OVR bool SetResource(qgWidgetElement nth, const qbWidgetResource& res);

	void SetEventPositionChanged(const qwEventIntFunc& cb) { _ecb_position_changed = cb; }

	void Scroll(int delta);

protected:
	K_OVR void OnInitialize();

	K_OVR bool OnUpdate(float adv);
	K_OVR void OnDraw();

	K_OVR bool OnMouseMove(kint x, kint y, const tpl::Point& relative, kushort state);
	K_OVR bool OnMouseDown(kint x, kint y, kushort button);
	K_OVR bool OnMouseUp(kint x, kint y, kushort button);

	//
	void ValidateRange();
};


//////////////////////////////////////////////////////////////////////////
// list 
class KAPI qwList : public qwCtrl
{
	KCH_SUBCLASS(qwList, qwCtrl);

public:
	struct Item : public tpl::MemAlloc<>
	{
		tpl::String			str;
		kpointer			tag;

		tpl::Rect			bound;
		bool				select;
	};

	struct Selection
	{
		kint				index;
		tpl::Rect			bound;
		tpl::Point			margin;
		qbWidgetResource	res[2];	// 0=active, 1=inactive
	};

	typedef tpl::VecArray<Item*> ItemArray;

protected:
	tpl::Rect			_blist;

	qwScroll*			_scrl;

	Selection			_select;
	kint				_current;

	bool				_canmulti;
	bool				_isfocus;

	bool				_dragselect;
	bool				_dragging;		// of item

	kint				_scrlsize;
	kint				_height;		// of item
	tpl::Point			_anchor;		// of item
	tpl::Point			_border;

	ItemArray			_items;

	qwEventDragFunc		_ecb_drag_item_enter;
	qwEventDragFunc		_ecb_drag_item_leave;
	qwEventIntFunc		_ecb_select_changed;

protected:
	qwList(qgWidgetType type = QGW_LIST);
	~qwList();

public:
	void ClearItems();
	bool AddItem(const char* str, kpointer tag = NULL);
	bool InsertItem(kint nth, const char* str, kpointer tag = NULL);
	bool RemoveItem(kint nth);
	Item* GetItem(kint nth);
	kint GetItemCount() { return _items.Count(); }

	//
	void SetSelectedIndex(kint nth);
	kint GetSelectedIndex() { return _current; }
	Item* GetSelectedItem() { return _current >= 0 ? _items[_current] : NULL; }

	bool CanMultiSelect() { return _canmulti; }
	kint GetScrollSize() { return _scrlsize; }
	kint GetItemHeight() { return _height; }
	const tpl::Point& GetBorderSize() { return _border; }
	const tpl::Point& GetSelectMargin() { return _select.margin; }
	K_OVR const qbWidgetResource& GetResource(qgWidgetElement nth);

	//
	void SetMultiSelect(bool v) { _canmulti = v; }
	void SetScrollSize(kint size) { if (size > 0) _scrlsize = size; }
	void SetItemHeight(kint height) { if (height > 0) _height = height; }
	void SetBorderSize(const tpl::Point& size) { _border = size; }
	void SetSelectMargin(const tpl::Point& size) { _select.margin = size; }
	K_OVR bool SetResource(qgWidgetElement nth, const qbWidgetResource& res);

	void SetEventDragEnter(const qwEventDragFunc& cb) { _ecb_drag_item_enter = cb; }
	void SetEventDragLeave(const qwEventDragFunc& cb) { _ecb_drag_item_leave = cb; }
	void SetEventSelectChanged(const qwEventIntFunc& cb) { _ecb_select_changed = cb; }

protected:
	K_OVR void OnInitialize();

	K_OVR bool OnUpdate(float adv);
	K_OVR void OnDraw();

	K_OVR void OnChangeFocus(bool focus);

	K_OVR bool OnMouseMove(kint x, kint y, const tpl::Point& relative, kushort state);
	K_OVR bool OnMouseDown(kint x, kint y, kushort button);
	K_OVR bool OnMouseUp(kint x, kint y, kushort button);
	K_OVR bool OnMouseWheel(kshort delta);
	K_OVR bool OnKeyDown(kbyte key, kbyte sub, kuint ch, bool repeat, kushort state);
};


//////////////////////////////////////////////////////////////////////////
// combo
class KAPI qwCombo : public qwLabel
{
	KCH_SUBCLASS(qwCombo, qwLabel);
	KCH_FRIEND(qwRibbonCombo);

public:
	enum Element
	{
		ElemOpen,
		ElemDown,
		ElemMaxValue
	};

	struct Item : public tpl::MemAlloc<>
	{
		tpl::String			str;
		kpointer			tag;

		tpl::ColorValue		color;
	};

	struct Selection
	{
		kint				focus;
		tpl::Rect			bound;
		tpl::Point			margin;
		qbWidgetResource	res[2];		// share with list's item resource
	};

	typedef tpl::VecArray<Item*> ItemArray;

protected:
	tpl::Rect			_bsave;
	tpl::Rect			_blist;
	tpl::Rect			_blabel;
	tpl::Rect			_belem[ElemMaxValue];
	qbWidgetResource	_relem[ElemMaxValue];

	qwScroll*			_scrl;

	Selection			_select;
	kint				_current;

	bool				_isopen;
	bool				_isfocus;
	bool				_autoheight;

	kint				_scrlsize;
	kint				_droplen;
	kint				_height;		// of item
	tpl::Point			_anchor;		// of item
	tpl::Point			_border;

	ItemArray			_items;

	qwEventBoolFunc		_ecb_context_open;
	qwEventBoolFunc		_ecb_context_close;
	qwEventIntFunc		_ecb_select_changed;

protected:
	qwCombo(qgWidgetType type = QGW_COMBO);
	~qwCombo();

public:
	void ClearItems();
	bool AddItem(const char* str, kpointer tag = NULL);
	bool AddItem(const char* str, const tpl::ColorValue& color, kpointer tag = NULL);
	bool InsertItem(kint nth, const char* str, kpointer tag = NULL);
	bool InsertItem(kint nth, const char* str, const tpl::ColorValue& color, kpointer tag = NULL);
	bool RemoveItem(kint nth);
	Item* GetItem(kint nth);
	kint GetItemCount() { return _items.Count(); }

	//
	void SetSelectedIndex(kint nth);
	kint GetSelectedIndex() { return _current; }
	Item* GetSelectedItem() { return _current >= 0 ? _items[_current] : NULL; }

	bool IsOpen() { return _isopen; }
	kint GetScrollSize() { return _scrlsize; }
	kint GetDropLength() { return _droplen; }
	kint GetItemHeight() { return _height; }
	const tpl::Point& GetBorderSize() { return _border; }
	const tpl::Point& GetSelectMargin() { return _select.margin; }
	K_OVR const qbWidgetResource& GetResource(qgWidgetElement nth);

	//
	void SetScrollSize(kint size) { if (size > 0) _scrlsize = size; }
	void SetDropLength(kint len) { if (len > 0) _droplen = len; }
	void SetItemHeight(kint height) { if (height > 0) _height = height; }
	void SetAutoHeight(bool value) { _autoheight = value; }
	void SetBorderSize(const tpl::Point& size) { _border = size; }
	void SetSelectMargin(const tpl::Point& size) { _select.margin = size; }
	K_OVR bool SetResource(qgWidgetElement nth, const qbWidgetResource& res);

	void SetEventContextOpen(const qwEventBoolFunc& cb) { _ecb_context_open = cb; }
	void SetEventContextClose(const qwEventBoolFunc& cb) { _ecb_context_close = cb; }
	void SetEventSelectChanged(const qwEventIntFunc& cb) { _ecb_select_changed = cb; }

protected:
	K_OVR void OnInitialize();
	K_OVR bool OnUpdate(float adv);
	K_OVR void OnDraw();

	K_OVR void OnChangeFocus(bool focus);

	K_OVR bool OnMouseMove(kint x, kint y, const tpl::Point& relative, kushort state);
	K_OVR bool OnMouseDown(kint x, kint y, kushort button);
	K_OVR bool OnMouseUp(kint x, kint y, kushort button);
	K_OVR bool OnMouseWheel(kshort delta);
	K_OVR bool OnKeyDown(kbyte key, kbyte sub, kuint ch, bool repeat, kushort state);
};


//////////////////////////////////////////////////////////////////////////
// text line
class KAPI qwTextLine : public qwLabel
{
	KCH_SUBCLASS(qwTextLine, qwLabel);

public:
	enum Element
	{
		ElemDisp,
		ElemCaret,
		ElemSelect,
		ElemInvert,
		ElemMaxValue
	};

	struct CaretInfo
	{
		float			adv;
		float			blink;
		bool			visible;
	};

	struct ScribeInfo
	{
		qgScribeFontRef	scribe;
		kint			first;
		kint			right;
		kint			caret;
		kint			select;
		tpl::Point		range;
	};

protected:
	tpl::Rect			_bdisp;			// display area of input
	qbWidgetResource	_relem[ElemMaxValue];

	bool				_drag;			// drag text
	bool				_insmode;		// insert mode
	bool				_isfocus;
	bool				_onlynum;

	kint				_carpos;		// caret position
	kint				_vispos;		// first visible position
	kint				_selpos;		// first select position

	CaretInfo			_caret;
	ScribeInfo			_scribe;

	kint				_height;		// anchor height
	tpl::Point			_border;

	qwEventBoolFunc		_ecb_text_cmd;

protected:
	qwTextLine(qgWidgetType type = QGW_TEXTLINE);
	~qwTextLine();

public:
	bool NumberOnly() { return _onlynum; }
	bool GetInsertMode() { return _insmode; }
	float GetCaretBlink() { return _caret.blink; }
	const tpl::Point& GetBorderSize() { return _border; }
	K_OVR const qbWidgetResource& GetResource(qgWidgetElement nth);

	//
	void SetNumberOnly(bool v) { _onlynum = v; }
	void SetInsertMode(bool v) { _insmode = v; }
	void SetCaretBlink(float blink) { if (blink > 0.0f) _caret.blink = blink; }
	void SetBorderSize(const tpl::Point& size) { _border = size; }
	K_OVR bool SetResource(qgWidgetElement nth, const qbWidgetResource& res);

	//
	void CopyText();
	void PasteText();

	void ResetCaret();
	void SetCaretPlace(kint nth);
	void SetSelection(kint start, kint end = -1);
	void ClearSelection();

	void SetEventTextCommand(const qwEventBoolFunc& cb) { _ecb_text_cmd = cb; }

protected:
	K_OVR void OnInitialize();

	K_OVR bool OnUpdate(float adv);
	K_OVR void OnDraw();

	K_OVR void OnChangeFocus(bool focus);
	K_OVR void OnChangeFont(qgFont* font);

	K_OVR void OnTextChanged(const char* str, kint len);

	K_OVR bool OnMouseMove(kint x, kint y, const tpl::Point& relative, kushort state);
	K_OVR bool OnMouseDown(kint x, kint y, kushort button);
	K_OVR bool OnMouseUp(kint x, kint y, kushort button);
	K_OVR bool OnMouseDouble(kint x, kint y, kushort button);
	K_OVR bool OnKeyDown(kbyte key, kbyte sub, kuint ch, bool repeat, kushort state);
	K_OVR bool OnChar(kint ch, bool repeat);
};


//////////////////////////////////////////////////////////////////////////
// panel
class KAPI qwPanel : public qwLabel, public qbWidgetContainer
{
	KCH_SUBCLASS(qwPanel, qwLabel);

public:
	typedef tpl::VecArray<qwCtrl*> ControlArray;

	struct Background
	{
		bool				visible;	// is workarea visible?
		qgTexture*			tex;
		qbWidgetResource	res;
	};

protected:
	Background			_bg;

	ControlArray		_ctls;

	qwCtrl*			_wselect;
	qwCtrl*			_wover;
	qwCtrl*			_wactive;

	kuint				_depth;

protected:
	qwPanel(qgWidgetType type = QGW_PANEL);
	~qwPanel();

public:
	K_OVR qbWidgetContainer* GetContainer() { return this; }
	K_OVR const qbWidgetResource& GetResource(qgWidgetElement nth);

	void SetBackground(qgTexture* tex);
	void SetBackColor(const tpl::ColorValue& color) { _bg.res.color = color; }
	K_OVR bool SetResource(qgWidgetElement nth, const qbWidgetResource& res);

	// [ IqwConteiner
	K_OVR qwCtrl* GetControl(const char* name);
	K_OVR qwCtrl* FindControl(kint id);

	K_OVR qwCtrl* GetControlNth(kint nth);
	K_OVR kint IndexControlOf(qwCtrl* cx);

	K_OVR bool AddControl(qwCtrl* cx);
	K_OVR bool RemoveControl(qwCtrl* cx);

	K_OVR void UnsafeAddControl(qwCtrl* cx);
	K_OVR bool UnsafeRemoveControl(qwCtrl* cx);

	K_OVR void ClearControls();
	K_OVR void SortControls(qwCtrl* primary);
	// ] IqwConteiner

	//
	qwCtrl* AddDivide(kint id, kint x, kint y, kint size);
	qwCtrl* AddLabel(kint id, kint x, kint y, kint w, kint h, const char* text = NULL);
	qwCtrl* AddButton(kint id, kint x, kint y, kint w, kint h, const char* text = NULL);
	qwCtrl* AddProgress(kint id, kint x, kint y, kint w, kint h, kint minv, kint maxv);
	qwCtrl* AddScroll(kint id, kint x, kint y, kint h, kint size);
	qwCtrl* AddList(kint id, kint x, kint y, kint w, kint h);
	qwCtrl* AddCombo(kint id, kint x, kint y, kint w, kint h, kint droplen);
	qwCtrl* AddTextLine(kint id, kint x, kint y, kint w, kint h, const char* text = NULL);
	qwCtrl* AddPanel(kint id, kint x, kint y, kint w, kint h);

protected:
	qwCtrl* PickControl(kint x, kint y);
	virtual void SetControl(qwCtrl* w, qgWidgetStatus status);

protected:
	K_OVR void OnInitialize();

	K_OVR bool OnUpdate(float adv);
	K_OVR void OnDraw();

	K_OVR bool OnMouseMove(kint x, kint y, const tpl::Point& relative, kushort state);
	K_OVR bool OnMouseDown(kint x, kint y, kushort button);
	K_OVR bool OnMouseUp(kint x, kint y, kushort button);
	K_OVR bool OnMouseDouble(kint x, kint y, kushort button);
	K_OVR bool OnMouseWheel(kshort delta);
	K_OVR bool OnKeyDown(kbyte key, kbyte sub, kuint ch, bool repeat, kushort state);
	K_OVR bool OnKeyUp(kbyte key, kbyte extend, kushort state);
	K_OVR bool OnChar(kint ch, bool repeat);
};


//////////////////////////////////////////////////////////////////////////
// form
class KAPI qwForm : public qwPanel
{
	KCH_SUBCLASS(qwForm, qwPanel);

public:
	struct Title
	{
		qgTexture*			icon;
		qbWidgetResource	rico;
		tpl::Rect			bound;
		tpl::BlendColor		blend;
		kint				height;
	};

	struct Sizer
	{
		tpl::Point			minsize;
		tpl::Point			maxsize;
		tpl::Rect			bound;
		qbWidgetResource	res;
	};

protected:
	tpl::Rect			_bsave;

	Title				_title;
	Sizer				_sizer;

	tpl::Point			_delta;

protected:
	qwForm(qgWidgetType type = QGW_FORM);
	~qwForm();

public:
	bool TestMoving() { return _insts.moving; }
	bool TestSizing() { return _insts.sizing; }
	bool TestChangeLayout() { return _insts.moving || _insts.sizing; }
	K_OVR const qbWidgetResource& GetResource(qgWidgetElement nth);

	void SetIcon(qgTexture* tex);
	void SetMinimumSize(const tpl::Point& value) { _sizer.minsize = value; }
	void SetMaximumSize(const tpl::Point& value) { _sizer.maxsize = value; }
	K_OVR bool SetResource(qgWidgetElement nth, const qbWidgetResource& res);

protected:
	K_OVR void OnInitialize();

	K_OVR bool OnUpdate(float adv);
	K_OVR void OnDraw();

	K_OVR void OnChangeStatus(qgWidgetStatus status);

	K_OVR bool OnMouseMove(kint x, kint y, const tpl::Point& relative, kushort state);
	K_OVR bool OnMouseDown(kint x, kint y, kushort button);
	K_OVR bool OnMouseUp(kint x, kint y, kushort button);
};


//////////////////////////////////////////////////////////////////////////
// ribbon button (44x66, 62x22)
class KAPI qwRibbonButton : public qwButton
{
	KCH_SUBCLASS(qwRibbonButton, qwButton);

protected:
	qgWidgetDirection	_dir;

	tpl::Rect			_icon_bound;
	tpl::Quad			_icon_coord;

protected:
	qwRibbonButton(qgWidgetType type = QGW_RIBBON_BUTTON);
	~qwRibbonButton() {};

public:
	void SetDirection(qgWidgetDirection dir);		// HORIZ, VERT only

protected:
	K_OVR bool OnUpdate(float adv);
	K_OVR void OnDraw();
};


//////////////////////////////////////////////////////////////////////////
// ribbon combo 
class KAPI qwRibbonCombo : public qwLabel
{
	KCH_SUBCLASS(qwRibbonCombo, qwLabel);

protected:
	qgWidgetDirection	_dir;

	tpl::Rect			_icon_bound;
	tpl::Quad			_icon_coord;

	qwCombo*			_cbo;

	qwEventBoolFunc		_ecb_context_open;
	qwEventBoolFunc		_ecb_context_close;
	qwEventIntFunc		_ecb_select_changed;

protected:
	qwRibbonCombo(qgWidgetType type = QGW_RIBBON_COMBO);
	~qwRibbonCombo();

public:
	qwCombo* GetCombo() { return _cbo; }

	void SetDirection(qgWidgetDirection dir);		// HORIZ, VERT only

	void ClearItems();
	bool AddItem(const char* str, kpointer tag = NULL);
	bool AddItem(const char* str, const tpl::ColorValue& color, kpointer tag = NULL);
	bool InsertItem(kint nth, const char* str, kpointer tag = NULL);
	bool InsertItem(kint nth, const char* str, const tpl::ColorValue& color, kpointer tag = NULL);
	bool RemoveItem(kint nth);
	qwCombo::Item* GetItem(kint nth);
	kint GetItemCount() { return _cbo->GetItemCount(); }

	//
	void SetSelectedIndex(kint nth) { _cbo->SetSelectedIndex(nth); }
	kint GetSelectedIndex() { return _cbo->GetSelectedIndex(); }
	qwCombo::Item* GetSelectedItem() { return _cbo->GetSelectedItem(); }

	bool IsOpen() { return _cbo->IsOpen(); }
	kint GetScrollSize() { return _cbo->GetScrollSize(); }
	kint GetDropLength() { return _cbo->GetDropLength(); }
	kint GetItemHeight() { return _cbo->GetItemHeight(); }

	//
	void SetScrollSize(kint size) { _cbo->SetScrollSize(size); }
	void SetDropLength(kint len) { _cbo->SetDropLength(len); }
	void SetItemHeight(kint height) { _cbo->SetItemHeight(height); }
	void SetAutoHeight(bool value) { _cbo->SetAutoHeight(value); }

	void SetEventContextOpen(const qwEventBoolFunc& cb) { _ecb_context_open = cb; }
	void SetEventContextClose(const qwEventBoolFunc& cb) { _ecb_context_close = cb; }
	void SetEventSelectChanged(const qwEventIntFunc& cb) { _ecb_select_changed = cb; }

protected:
	K_OVR void OnInitialize();

	K_OVR bool OnUpdate(float adv);
	K_OVR void OnDraw();

	K_OVR void OnChangeFocus(bool focus);

	K_OVR bool OnMouseMove(kint x, kint y, const tpl::Point& relative, kushort state);
	K_OVR bool OnMouseDown(kint x, kint y, kushort button);
	K_OVR bool OnMouseUp(kint x, kint y, kushort button);
	K_OVR bool OnMouseWheel(kshort delta);
};

#endif	// __INC_QGUWGS_H__
